package com.lmk.ms.common.mvc.controller;

import com.lmk.ms.common.constants.StatusEnum;
import com.lmk.ms.common.dto.db.PageResult;
import com.lmk.ms.common.dto.db.QueryParams;
import com.lmk.ms.common.dto.db.Search;
import com.lmk.ms.common.dto.db.Sort;
import com.lmk.ms.common.dto.mvc.IdParameter;
import com.lmk.ms.common.dto.mvc.PageData;
import com.lmk.ms.common.dto.mvc.ResponseResult;
import com.lmk.ms.common.entity.PkEntity;
import com.lmk.ms.common.exception.BizException;
import com.lmk.ms.common.log.annotation.ApiLog;
import com.lmk.ms.common.log.constants.LogAction;
import com.lmk.ms.common.mvc.service.BaseService;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.io.Serializable;
import java.util.List;

/**
 * 基类控制器
 *
 * @author laomake@hotmail.com
 * @version 1.0
 * @date 2021/07/12
 */
@Slf4j
public abstract class BaseController<S extends BaseService<T>, T extends PkEntity<PK>, PK extends Serializable> {

    @Autowired
    protected S baseService;

    /**
     * 根据ID获取单个对象
     *
     * @param id ID，注意路径变量只能通过正则表达式校验
     *           这里配置了不以0开头的数字
     * @return
     */
    @Operation(summary = "根据ID获取单个对象")
    @GetMapping("{id:[1-9]\\d*}")
    public ResponseResult<T> findById(@PathVariable("id") PK id, HttpServletRequest request) {
        T entity = baseService.getById(id);
        // 调用子类重写后的回调函数
        afterGet(id, entity, request);
        return ResponseResult.success(entity);
    }

    /**
     * 分页查询
     *
     * @param queryParams 参数
     * @param request
     * @return
     */
    @Operation(summary = "分页查询")
    @GetMapping("")
    public ResponseResult<PageData<T>> loadList(@Validated QueryParams queryParams, HttpServletRequest request) {

        // 解析查询条件
        List<Search> searchList = Search.parseSearch(queryParams.getSearch());
        List<Sort> sortList = Sort.parseSort(queryParams.getSort());

        // 设置查询字段
        String[] columns = queryParams.getColumns();
        if (columns == null || columns.length == 0) {
            queryParams.setColumns(new String[]{"*"});
        }

        // 调用子类重写的回调函数
        beforeList(queryParams, searchList, sortList, request);

        // 执行查询
        PageResult<T> page = baseService.pageList(queryParams, searchList, sortList);

        // 调用子类重写后的回调函数
        afterList(queryParams, page, request);

        PageData<T> pd = new PageData<>(baseService.loadMetaInfo(), page);
        return ResponseResult.success(pd);
    }

    @Operation(summary = "分页查询，只返回数据")
    @GetMapping("page")
    public PageResult<T> pageList(@Validated QueryParams queryParams, HttpServletRequest request) {

        // 解析查询条件
        List<Search> searchList = Search.parseSearch(queryParams.getSearch());
        List<Sort> sortList =  Sort.parseSort(queryParams.getSort());

        // 设置查询字段
        String[] columns = queryParams.getColumns();
        if (columns == null || columns.length == 0) {
            queryParams.setColumns(new String[]{"*"});
        }

        // 调用子类重写的回调函数
        beforeList(queryParams, searchList, sortList, request);

        // 执行查询
        PageResult<T> page = baseService.pageList(queryParams, searchList, sortList);

        // 调用子类重写后的回调函数
        afterList(queryParams, page, request);

        return page;
    }

    /**
     * 创建对象
     *
     * @param entity
     * @param request
     * @return
     */
    @ApiLog(title = "添加数据库记录", action = LogAction.ADD)
    @Operation(summary = "创建对象")
    @PostMapping("")
    public ResponseResult<PK> create(@RequestBody @Validated T entity, HttpServletRequest request) {
        ResponseResult<PK> result = null;

        // 调用子类重写的回调函数，可以补充部分参数，或进行数据校验
        StatusEnum status = beforeCreate(entity, request);
        if (status != StatusEnum.SUCCESS) {
            return ResponseResult.error(status);
        }

        boolean success = baseService.save(entity);
        if (success) {
            // 调用子类重写的回调函数
            status = afterCreate(entity, request);

            if (status != StatusEnum.SUCCESS) {
                ResponseResult.error(status);
            }
        } else {
            ResponseResult.error(StatusEnum.INTERNAL_SERVER_ERROR);
        }

        return ResponseResult.success(entity.getId());
    }

    /**
     * 更新对象
     *
     * @param entity
     * @param request
     * @return
     */
    @ApiLog(title = "修改数据库记录", action = LogAction.MODIFY)
    @Operation(summary = "更新对象")
    @PutMapping("")
    public ResponseResult<?> update(@RequestBody @Validated T entity, HttpServletRequest request) {
        // 校验主键ID是否有效
        PK id = entity.getId();
        if (id == null) {
            BizException.throw200(StatusEnum.BAD_REQUEST_ParameterNotValid, "id不可以为空");
        }

        T dbEntity = baseService.getById(id);
        if (dbEntity == null) {
            BizException.throw200(StatusEnum.BAD_REQUEST, "没有找到需要更新的对象");
        }

        // 调用子类重写的回调函数，可以补充部分参数，或进行数据校验
        StatusEnum status = beforeUpdate(entity, dbEntity, request);
        if (status != StatusEnum.SUCCESS) {
            return ResponseResult.error(status);
        }
        boolean success = baseService.updateById(entity);
        if (success) {
            // 调用子类重写的回调函数
            status = afterUpdate(entity, dbEntity, request);
        } else {
            ResponseResult.error(StatusEnum.INTERNAL_SERVER_ERROR);
        }

        return ResponseResult.status(status);
    }

    /**
     * 更新对的状态，主要用启用或停用对象，仅针对RecordEntity的子类对象起作用
     * @param parameter
     * @return
     */
    @ApiLog(title = "修改数据库记录状态", action = LogAction.MODIFY)
    @Operation(summary = "更新对象的状态")
    @PutMapping("change/status")
    public ResponseResult<Boolean> changeStatus(@RequestBody @Validated IdParameter parameter) {
        Boolean result = baseService.changeDbStatus(parameter);
        return ResponseResult.success(result);
    }

    /**
     * 根据ID删除
     *
     * @param id      ID，注意路径变量只能通过正则表达式校验
     *                这里配置了不以0开头的数字
     * @param request
     * @return
     */
    @ApiLog(title = "删除数据库记录", action = LogAction.REMOVE)
    @Operation(summary = "根据ID删除")
    @DeleteMapping("{id:[1-9]\\d*}")
    public ResponseResult<?> delete(@PathVariable("id") PK id, HttpServletRequest request) {
        // 校验主键ID是否有效
        if (id == null) {
            BizException.throw200(StatusEnum.BAD_REQUEST_ParameterNotValid, "id不可以为空");
        }

        T dbEntity = baseService.getById(id);
        if (dbEntity == null) {
            BizException.throw200(StatusEnum.BAD_REQUEST, "没有找到需要删除的对象");
        }

        // 调用子类重写的回调函数，可以补充部分参数，或进行数据校验
        StatusEnum status = beforeDelete(dbEntity, request);
        if (status != StatusEnum.SUCCESS) {
            return ResponseResult.error(status);
        }

        boolean success = baseService.removeById(id);
        if (success) {
            // 调用子类重写的回调函数
            status = afterDelete(dbEntity, request);
        } else {
            ResponseResult.error(StatusEnum.INTERNAL_SERVER_ERROR);
        }

        return ResponseResult.status(status);
    }

    /**
     * 回调函数，在执行列表查询之前调用，可以修改过滤条件及排序条件
     *
     * @param queryParams 原始参数，包含分页、查询、排序等参数
     * @param searchList  解析后的查询条件列表
     * @param sortList    解析后的排序条件列表
     * @param request
     */
    protected void beforeList(QueryParams queryParams, List<Search> searchList, List<Sort> sortList, HttpServletRequest request) {
    }

    /**
     * 回调函数，在执行列表查询之后调用
     *
     * @param queryParams
     * @param page
     * @param request
     */
    protected void afterList(QueryParams queryParams, PageResult<T> page, HttpServletRequest request) {
    }

    /**
     * 回调函数，在执行ID查询之后调用
     * @param id
     * @param entity
     * @param request
     */
    protected void afterGet(PK id, T entity, HttpServletRequest request) {
    }

    /**
     * 回调函数：在保存新对象之前调用，可以继续设置entity属性
     *
     * @param entity
     * @param request
     * @return 校验码，仅StatusCode.Success为校验通过
     */
    protected StatusEnum beforeCreate(T entity, HttpServletRequest request) {
        return StatusEnum.SUCCESS;
    }

    /**
     * 回调函数：在保存新对象之后调用
     *
     * @param entity
     * @param request
     */
    protected StatusEnum afterCreate(T entity, HttpServletRequest request) {
        return StatusEnum.SUCCESS;
    }

    /**
     * 回调函数：在更新对象之前调用，可以继续设置entity属性
     *
     * @param entity
     * @param dbEntity
     * @param request
     * @return
     */
    protected StatusEnum beforeUpdate(T entity, T dbEntity, HttpServletRequest request) {
        return StatusEnum.SUCCESS;
    }

    /**
     * 回调函数：在更新对象之后调用
     *
     * @param entity
     * @param dbEntity
     * @param request
     */
    protected StatusEnum afterUpdate(T entity, T dbEntity, HttpServletRequest request) {
        return StatusEnum.SUCCESS;
    }

    /**
     * 回调函数：在删除对象之前调用，可以校验用户权限
     *
     * @param dbEntity
     * @param request
     * @return 校验码，仅StatusCode.Success为校验通过
     */
    protected StatusEnum beforeDelete(T dbEntity, HttpServletRequest request) {
        return StatusEnum.SUCCESS;
    }

    /**
     * 回调函数：在删除对象之后调用
     *
     * @param dbEntity
     * @param request
     */
    protected StatusEnum afterDelete(T dbEntity, HttpServletRequest request) {
        return StatusEnum.SUCCESS;
    }
}